package org.budo.execute.limit;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Semaphore;

import org.budo.execute.limit.annotation.ExecuteLimit;
import org.springframework.core.annotation.AnnotationUtils;

/**
 * @author limingwei
 */
public class AbstractExecuteLimitInterceptor {
    private static Map<Method, Semaphore> _methodSemaphoreCache = new ConcurrentHashMap<Method, Semaphore>();

    private Integer defaultExecuteLimit = null;

    public Integer getDefaultExecuteLimit() {
        return this.defaultExecuteLimit;
    }

    public void setDefaultExecuteLimit(Integer defaultExecuteLimit) {
        this.defaultExecuteLimit = defaultExecuteLimit;
    }

    private Integer getExecuteLimitValue(ExecuteLimit annotation) {
        if (null != annotation && annotation.value() > 0) {
            return annotation.value();
        }

        return this.getDefaultExecuteLimit();
    }

    private Semaphore getMethodSemaphoreCached(Method method, Integer limit) {
        Semaphore _semaphore = _methodSemaphoreCache.get(method);
        if (null != _semaphore) {
            return _semaphore;
        }

        synchronized (method) {
            _semaphore = _methodSemaphoreCache.get(method);
            if (null != _semaphore) {
                return _semaphore;
            }

            _semaphore = new Semaphore(limit);
            _methodSemaphoreCache.put(method, _semaphore);
            return _semaphore;
        }
    }

    protected Semaphore getMethodSemaphore(Method method) {
        ExecuteLimit annotation = AnnotationUtils.findAnnotation(method, ExecuteLimit.class);

        Integer executeLimitValue = this.getExecuteLimitValue(annotation);
        if (null == executeLimitValue || executeLimitValue < 0) {
            return null;
        }

        return this.getMethodSemaphoreCached(method, executeLimitValue);
    }
}